package com.controller;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;

import org.apache.log4j.Logger;
import org.primefaces.event.SelectEvent;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.web.jsf.FacesContextUtils;

import com.builder.DepositoBuilder;
import com.builder.ProductoBuilder;
import com.builder.SeccionBuilder;
import com.dao.BaseModelDAO;
import com.model.Cliente;
import com.model.Deposito;
import com.model.Producto;
import com.model.Seccion;
import com.query.DepositoQuery;
import com.type.TipoDepositoEnum;
import com.util.JSFUtil;
import com.util.ValidatorUtil;
import com.view.ClienteView;
import com.view.DepositoView;
import com.view.ProductoView;
import com.view.SeccionProductoView;
import com.view.SeccionView;

@ManagedBean
@ViewScoped
@SuppressWarnings("serial")
public class DepositoController extends PaginableController<Deposito> {

	private Logger log = Logger.getLogger(DepositoController.class);
	private BaseModelDAO<Deposito> dao;
	private BaseModelDAO<Cliente> clienteDao;
	private BaseModelDAO<Seccion> seccionDao;
	private BaseModelDAO<Producto> productoDao;
	private Deposito model;
	private DepositoQuery query;
	private List<TipoDepositoEnum> listaTipoDeposito;
	private List<SelectItem> clienteSIL;
	private List<SeccionView> listaSeccion;
	private DataModel<SeccionProductoView> seccionProductoDM;
	private DataModel<ProductoView> productoDM;
	private List<SeccionProductoView> listaSeccionProducto;

	@ManagedProperty("#{depositoView}")
	private DepositoView view;

	@ManagedProperty("#{depositoBuilder}")
	private DepositoBuilder builder;

	@ManagedProperty("#{seccionBuilder}")
	private SeccionBuilder seccionBuilder;

	@ManagedProperty("#{seccionProductoView}")
	private SeccionProductoView seccionProductoView;

	@ManagedProperty("#{productoView}")
	private ProductoView productoView;

	@ManagedProperty("#{productoBuilder}")
	private ProductoBuilder productoBuilder;

	@SuppressWarnings("unchecked")
	public DepositoController() {

		try {
			dao = (BaseModelDAO<Deposito>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("depositoDAO");
			clienteDao = (BaseModelDAO<Cliente>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("clienteDAO");
			seccionDao = (BaseModelDAO<Seccion>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("seccionDAO");
			productoDao = (BaseModelDAO<Producto>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("productoDAO");

			seccionProductoDM = new ListDataModel<SeccionProductoView>();
			productoDM = new ListDataModel<ProductoView>();
			listaSeccionProducto = new ArrayList<SeccionProductoView>();

			query = new DepositoQuery();
			listaTipoDeposito = Arrays.asList(TipoDepositoEnum.values());

			clienteSIL = new ArrayList<SelectItem>();
			for (Cliente cliente : clienteDao.getList()) {
				clienteSIL.add(new SelectItem(cliente.getID(), cliente
						.getNombre()));
			}

			addEdit = false;

		} catch (Throwable e) {
			log.error("Error al inicializar la clase DepositoController", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	public DepositoQuery getQuery() {
		return query;
	}

	public void setQuery(DepositoQuery query) {
		this.query = query;
	}

	public Deposito getModel() {
		return model;
	}

	public void setModel(Deposito model) {
		this.model = model;
	}

	public DepositoView getView() {
		return view;
	}

	public void setView(DepositoView view) {
		this.view = view;
	}

	public DepositoBuilder getBuilder() {
		return builder;
	}

	public void setBuilder(DepositoBuilder builder) {
		this.builder = builder;
	}

	public List<TipoDepositoEnum> getListaTipoDeposito() {
		return listaTipoDeposito;
	}

	public void setListaTipoDeposito(List<TipoDepositoEnum> listaTipoDeposito) {
		this.listaTipoDeposito = listaTipoDeposito;
	}

	public List<SelectItem> getClienteSIL() {
		return clienteSIL;
	}

	public SeccionProductoView getSeccionProductoView() {
		return seccionProductoView;
	}

	public void setSeccionProductoView(SeccionProductoView seccionProductoView) {
		this.seccionProductoView = seccionProductoView;
	}

	public SeccionBuilder getSeccionBuilder() {
		return seccionBuilder;
	}

	public void setSeccionBuilder(SeccionBuilder seccionBuilder) {
		this.seccionBuilder = seccionBuilder;
	}

	public List<SeccionView> getListaSeccion() {

		if (listaSeccion == null) {
			listaSeccion = seccionBuilder.toView(seccionDao.getList());
		}
		return listaSeccion;
	}

	public ProductoView getProductoView() {
		return productoView;
	}

	public void setProductoView(ProductoView productoView) {
		this.productoView = productoView;
	}

	public ProductoBuilder getProductoBuilder() {
		return productoBuilder;
	}

	public void setProductoBuilder(ProductoBuilder productoBuilder) {
		this.productoBuilder = productoBuilder;
	}

	public DataModel<SeccionProductoView> getSeccionProductoDM() {
		return seccionProductoDM;
	}

	public DataModel<ProductoView> getProductoDM() {
		return productoDM;
	}

	@Override
	public void query(ActionEvent event) {
		loadList();
	}

	@Override
	public void edit(ActionEvent event) {

		try {
			model = lazyDM.getRowData();
			model = dao.findFULL(model.getID());
			view = builder.toView(model);
			listaSeccionProducto = view.getListaSeccionProducto();
			seccionProductoDM = new ListDataModel<SeccionProductoView>(
					listaSeccionProducto);
			addEdit = true;
		} catch (Throwable e) {
			log.error("Error al editar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}

	}

	@Override
	public void delete(ActionEvent event) {

		try {
			model = lazyDM.getRowData();
			dao.delete(model);
			loadList();
		} catch (Throwable e) {
			log.error("Error al eliminar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@Override
	public void add(ActionEvent event) {
		clear();
		addEdit = true;
	}

	@Override
	public void save(ActionEvent event) {

		try {

			view.setListaSeccionProducto(listaSeccionProducto);

			List<String> errores = new ValidatorUtil().validate(view);

			if (errores.size() == 0) {

				model = builder.toDomain(view);

				if (model.getCliente().getID() == null) {
					model.setCliente(null);
				}

				if (model.getID() != null) {
					dao.edit(model);
					addEdit = false;
				} else {
					dao.save(model);
				}
				clear();
				JSFUtil.saveMessage("Elemento guardado con exito",
						FacesMessage.SEVERITY_INFO);
				if (!addEdit) {
					loadList();
				}
			} else {
				JSFUtil.saveMessages(errores);
			}
		} catch (DataIntegrityViolationException e) {
			JSFUtil.saveMessage(
					"El nombre del elemento ya existe en el sistema.",
					FacesMessage.SEVERITY_ERROR);
		} catch (Throwable e) {
			log.error("Error al guardar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@Override
	public void cancel(ActionEvent event) {
		addEdit = false;
		lazyDM = null;
		clear();
	}

	@Override
	public void clear() {
		view = new DepositoView();
		model = new Deposito();
		seccionProductoView = new SeccionProductoView();
		productoView = new ProductoView();
		listaSeccionProducto = new ArrayList<SeccionProductoView>();
		seccionProductoDM = new ListDataModel<SeccionProductoView>();
	}

	private void loadList() {

		lazyDM = new LazyDataModel<Deposito>() {
			@Override
			public List<Deposito> load(int first, int pageSize,
					String sortField, SortOrder sortOrder,
					Map<String, Object> filters) {

				Map<String, Object> filtro = new HashMap<String, Object>();
				filtro.put("nombre", query.getNombre());
				filtro.put("tipoDeposito", query.getTipoDeposito());

				return dao.getList(first, pageSize, "nombre", true, filtro,
						true);
			}
		};

		Map<String, Object> filtro = new HashMap<String, Object>();
		filtro.put("nombre", query.getNombre());
		filtro.put("tipoDeposito", query.getTipoDeposito());
		lazyDM.setRowCount(dao.count(filtro, true).intValue());
	}

	public boolean isCliente() {
		return TipoDepositoEnum.CLIENTE.equals(view.getTipoDeposito());
	}

	public void tipoDepositoListener() {
		System.out.println("Tipo de deposito seleccionado: "
				+ view.getTipoDeposito());
		view.setCliente(new ClienteView());
	}

	public List<ProductoView> completeProducto(String query) {
		List<ProductoView> listaProducto = null;
		try {

			List<Producto> listaProductosEntity = productoDao.getList(query);
			if (listaProductosEntity != null && listaProductosEntity.size() > 0) {
				listaProducto = productoBuilder.toView(listaProductosEntity);
			}

		} catch (Throwable e) {
			log.error("Error en le metodo completeProducto: ", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
		return listaProducto;
	}

	public boolean isProductoSelected() {
		return (productoView.getId() != null);
	}

	public void deselecionarProducto(ActionEvent event) {
		productoView = new ProductoView();
	}

	public void productoSelectChange(SelectEvent event) {
		productoView = (ProductoView) event.getObject();
		System.out.println("se eligio una un producto "
				+ productoView.getDetalle());
	}

	public void addDetalle(ActionEvent event) {

		List<String> errores = null;

		try {
			if (seccionProductoView.getSeccion().getId() == null) {
				seccionProductoView.setSeccion(null);
			}
			errores = new ValidatorUtil().validate(seccionProductoView);
		} catch (Throwable t) {
			log.error("Error al validar", t);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}

		if (errores.size() == 0) {
			// pongo los datos completos de las sección sin ir a la base
			for (SeccionView sv : listaSeccion) {
				if (seccionProductoView.getSeccion().getId().intValue() == sv
						.getId().intValue()) {
					seccionProductoView.setSeccion(sv);
					break;
				}
			}

			listaSeccionProducto.add(seccionProductoView);
			seccionProductoDM = new ListDataModel<SeccionProductoView>(
					listaSeccionProducto);

			seccionProductoView = new SeccionProductoView();
			productoDM = new ListDataModel<>(
					seccionProductoView.getListaProductos());
			productoView = new ProductoView();
		} else {
			JSFUtil.saveMessages(errores);
		}

		seccionProductoView.setSeccion(new SeccionView());

	}

	public void deleteDetalle(ActionEvent event) {
		SeccionProductoView detalle = seccionProductoDM.getRowData();
		listaSeccionProducto.remove(detalle);

		seccionProductoDM = new ListDataModel<SeccionProductoView>(
				listaSeccionProducto);
	}

	public void addProducto(ActionEvent event) {

		if (!seccionProductoView.getListaProductos().contains(productoView)) {
			seccionProductoView.getListaProductos().add(productoView);
			productoDM = new ListDataModel<>(
					seccionProductoView.getListaProductos());
		} else {
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"El producto ya fué agregado.", ""));
		}
		productoView = new ProductoView();
	}

	public void deleteProducto(ActionEvent event) {
		ProductoView producto = productoDM.getRowData();
		seccionProductoView.getListaProductos().remove(producto);
		productoDM = new ListDataModel<ProductoView>(
				seccionProductoView.getListaProductos());
	}

	public void cerrar(ActionEvent event) {
		productoView = new ProductoView();
		// listaContactos = new ArrayList<ContactoView>();
		// contactoDM = new ListDataModel<ContactoView>();
	}

	public void handleSelect(SelectEvent event) {
		System.out.println("se eligio una un producto");
		// direccionView.setLocalidad((Localidad) event.getObject());
	}

}
